home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / r5update.zip / I18N_INP.C next >
C/C++ Source or Header  |  1991-10-05  |  12KB  |  382 lines

  1. /*
  2.  * i18n_input.c
  3.  *
  4.  * Written by David Flanagan.  Copyright 1991, O'Reilly && Associates.
  5.  * This program is freely distributable without licensing fees and
  6.  * is provided without guarantee or warranty expressed or implied.
  7.  * This program is -not- in the public domain.
  8.  *
  9.  * This program demonstrates some of the X11R5 internationalized text
  10.  * input functions.  It creates a very simple window, connects to an
  11.  * input method, and displays composed text obtained by calling
  12.  * XwcLookupString.  It backspaces when it receives the Backspace or
  13.  * Delete keysyms.
  14.  *
  15.  * To run this program successfully, you must have an input method running.
  16.  * Because there are no input methods as part of the core X11R5
  17.  * distribution, this may be difficult.  If your Xlib uses the Xsi
  18.  * implementation of the X11R5 internationalization features, you can
  19.  * use the input method in contrib/im/Xsi.  In order to run this program,
  20.  * I had to do the following:
  21.  * 
  22.  *     1) build everything in contrib/im/Xsi.
  23.  *     2) install everything in contrib/im/Xsi.  This involved
  24.  *        installing a number of files under /usr/local/lib/wnn, and
  25.  *        adding a new user "wnn" to the /etc/passwd file.
  26.  *     3) start the "translation server" contrib/im/Xsi/Wnn/jserver/jserver
  27.  *     4) start the "input manager" contrib/im/Xsi/Xwnmo/xwnmo/xwnmo
  28.  *        which was also installed in /usr/bin/X11.
  29.  *     5) set the XMODIFIERS environment variable to "@im=_XWNMO".
  30.  *     6) set the LANG environment variable to something appropriate,
  31.  *        ja_JP.ujis, for example.
  32.  *
  33.  * With these steps accomplished, I was able to run the program and type
  34.  * Latin characters, but I was never able to figure out how to actually
  35.  * make use of the input method to input Japanese.   Since the Xsi input
  36.  * method is contributed software, it may have been updated since this
  37.  * program was written, and the above list may no longer be correct.
  38.  *
  39.  * This program has not been tested with the Ximp implementation.
  40.  *
  41.  * Finally, note that this program contains a work-around for a bug
  42.  * in the Xsi implementation of XwcLookupString.  If you are using
  43.  * the Ximp implementation, or if the bug has been fixed in your Xlib,
  44.  * you will need to undo the workaround.  See the comment below, near
  45.  * the call to XwcLookupString.
  46.  */
  47.  
  48. #include <stdio.h>
  49. #include <malloc.h>    
  50. #include <X11/Xlib.h>
  51. #include <X11/keysym.h>
  52. /*
  53.  * include <locale.h> or the non-standard X substitutes
  54.  * depending on the X_LOCALE compilation flag
  55.  */        
  56. #include <X11/Xlocale.h>  
  57.  
  58. /*
  59.  * This function chooses the "more desirable" of two input styles.  The
  60.  * style with the more complicated PreEdit style is returned, and if the
  61.  * styles have the same PreEdit styles, then the style with the more
  62.  * complicated Status style is returned.  There is no "official" way to
  63.  * order interaction styles.  This one makes the most sense to me.  
  64.  * This is a long procedure for a simple heuristic.
  65.  */
  66. XIMStyle ChooseBetterStyle(style1,style2)
  67. XIMStyle style1, style2;
  68. {
  69.     XIMStyle s,t;
  70.     XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks |
  71.     XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
  72.     XIMStyle status = XIMStatusArea | XIMStatusCallbacks |
  73.     XIMStatusNothing | XIMStatusNone;
  74.  
  75.     if (style1 == 0) return style2;
  76.     if (style2 == 0) return style1;
  77.     if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
  78.     return style1;
  79.     
  80.     s = style1 & preedit;
  81.     t = style2 & preedit;
  82.     if (s != t) {
  83.     if (s | t | XIMPreeditCallbacks)
  84.         return (s == XIMPreeditCallbacks)?style1:style2;
  85.     else if (s | t | XIMPreeditPosition)
  86.         return (s == XIMPreeditPosition)?style1:style2;
  87.     else if (s | t | XIMPreeditArea)
  88.         return (s == XIMPreeditArea)?style1:style2;
  89.     else if (s | t | XIMPreeditNothing)
  90.         return (s == XIMPreeditNothing)?style1:style2;
  91.     }
  92.     else { /* if preedit flags are the same, compare status flags */
  93.     s = style1 & status;
  94.     t = style2 & status;
  95.     if (s | t | XIMStatusCallbacks)
  96.         return (s == XIMStatusCallbacks)?style1:style2;
  97.     else if (s | t | XIMStatusArea)
  98.         return (s == XIMStatusArea)?style1:style2;
  99.     else if (s | t | XIMStatusNothing)
  100.         return (s == XIMStatusNothing)?style1:style2;
  101.     }
  102. }
  103.  
  104. void GetPreferredGeometry(ic, name, area)
  105. XIC ic;
  106. char *name;           /* XNPreEditAttributes or XNStatusAttributes */
  107. XRectangle *area;     /* the constraints on the area */
  108. {
  109.     XVaNestedList list;
  110.  
  111.     list = XVaCreateNestedList(0, XNAreaNeeded, area, NULL);
  112.  
  113.     /* set the constraints */
  114.     XSetICValues(ic, name, list, NULL);
  115.  
  116.     /* Now query the preferred size */
  117.     /* The Xsi input method, Xwnmo, seems to ignore the constraints, */
  118.     /* but we're not going to try to enforce them here. */
  119.     XGetICValues(ic, name, list, NULL);
  120.     XFree(list);
  121. }
  122.  
  123. void SetGeometry(ic, name, area)
  124. XIC ic;
  125. char *name;           /* XNPreEditAttributes or XNStatusAttributes */
  126. XRectangle *area;     /* the actual area to set */
  127. {
  128.     XVaNestedList list;
  129.  
  130.     list = XVaCreateNestedList(0, XNArea, area, NULL);
  131.     XSetICValues(ic, name, list, NULL);
  132.     XFree(list);
  133. }
  134.     
  135. main(argc, argv)
  136. int argc;
  137. char *argv[];
  138. {
  139.     Display *dpy;
  140.     int screen;
  141.     Window win;
  142.     GC gc;
  143.     XGCValues gcv;
  144.     XEvent event;
  145.     XFontSet fontset;
  146.     XIM im;
  147.     XIC ic;
  148.     XIMStyles *im_supported_styles;
  149.     XIMStyle app_supported_styles;
  150.     XIMStyle style;
  151.     XIMStyle best_style;
  152.     XVaNestedList list;
  153.     long im_event_mask;
  154.     XRectangle preedit_area;
  155.     XRectangle status_area;
  156.     char *program_name = argv[0];
  157.     char **missing_charsets;
  158.     int num_missing_charsets = 0;
  159.     char *default_string;
  160.     wchar_t string[200];
  161.     int str_len = 0;
  162.     int i;
  163.     
  164.     /*
  165.      * The error messages in this program are all in English.
  166.      * In a truely internationalized program, they would not
  167.      * be hardcoded; they would be looked up in a database of
  168.      * some sort.
  169.      */
  170.     if (setlocale(LC_ALL, "") == NULL) {
  171.         (void) fprintf(stderr, "%s: cannot set locale.\n",program_name);
  172.         exit(1);
  173.     }
  174.  
  175.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  176.     (void) fprintf(stderr, "%s: cannot open Display.\n", program_name);
  177.     exit(1);
  178.     }
  179.  
  180.     if (!XSupportsLocale()) {
  181.         (void) fprintf(stderr, "%s: X does not support locale \"%s\".\n",
  182.                        program_name, setlocale(LC_ALL, NULL));
  183.         exit(1);
  184.     }
  185.  
  186.     if (XSetLocaleModifiers("") == NULL) {
  187.         (void) fprintf(stderr, "%s: Warning: cannot set locale modifiers.\n",
  188.                                 argv[0]);
  189.     }
  190.  
  191.     /*
  192.      * Create the fontset.
  193.      */
  194.     fontset = XCreateFontSet(dpy,
  195.                  "-adobe-helvetica-*-r-*-*-*-120-*-*-*-*-*-*,\
  196.                               -misc-fixed-*-r-*-*-*-130-*-*-*-*-*-*",
  197.                  &missing_charsets, &num_missing_charsets,
  198.                  &default_string);
  199.     
  200.     /*
  201.      * if there are charsets for which no fonts can
  202.      * be found, print a warning message.  
  203.      */
  204.     if (num_missing_charsets > 0) {
  205.     (void)fprintf(stderr, "%s: The following charsets are missing:\n",
  206.               program_name);
  207.     for(i=0; i < num_missing_charsets; i++)
  208.         (void)fprintf(stderr, "%s: \t%s\n", program_name,
  209.               missing_charsets[i]);
  210.     XFreeStringList(missing_charsets);
  211.     
  212.     (void)fprintf(stderr, "%s: The string \"%s\" will be used in place\n",
  213.               program_name, default_string);
  214.     (void)fprintf(stderr, "%s: of any characters from those sets.\n",
  215.               program_name);
  216.     }
  217.  
  218.     screen = DefaultScreen(dpy);
  219.  
  220.     win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 0, 0, 400, 100,
  221.                  2, WhitePixel(dpy,screen),BlackPixel(dpy,screen));
  222.  
  223.     gc = XCreateGC(dpy,win,0,&gcv);
  224.     XSetForeground(dpy,gc,WhitePixel(dpy,screen));
  225.     XSetBackground(dpy,gc,BlackPixel(dpy,screen));
  226.  
  227.     /* Connect to an input method.  */
  228.     /* In this example, we don't pass a resource database */
  229.     if ((im = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) {
  230.     (void)fprintf(stderr, "Couldn't open input method\n");
  231.     exit(1);
  232.     }
  233.  
  234.     /* set flags for the styles our application can support */
  235.     app_supported_styles = XIMPreeditNone | XIMPreeditNothing | XIMPreeditArea;
  236.     app_supported_styles |= XIMStatusNone | XIMStatusNothing | XIMStatusArea;
  237.  
  238.     /* figure out which styles the IM can support */
  239.     XGetIMValues(im, XNQueryInputStyle, &im_supported_styles, NULL);
  240.  
  241.     /*
  242.      * now look at each of the IM supported styles, and
  243.      * chose the "best" one that we can support.
  244.      */
  245.     best_style = 0;
  246.     for(i=0; i < im_supported_styles->count_styles; i++) {
  247.     style = im_supported_styles->supported_styles[i];
  248.     if ((style & app_supported_styles) == style) /* if we can handle it */
  249.         best_style = ChooseBetterStyle(style, best_style);
  250.     }
  251.  
  252.  
  253.     /* if we couldn't support any of them, print an error and exit */
  254.     if (best_style == 0) {
  255.     (void)fprintf(stderr, "%s: application and program do not share a\n",
  256.               argv[0]);
  257.     (void)fprintf(stderr, "%s: commonly supported interaction style.\n",
  258.               argv[0]);
  259.     exit(1);
  260.     }
  261.     
  262.     /*
  263.      * Now go create an IC using the style we chose.
  264.      * Also set the window and fontset attributes now.
  265.      */
  266.     list = XVaCreateNestedList(0,XNFontSet,fontset,NULL);
  267.     ic = XCreateIC(im,
  268.            XNInputStyle, best_style,
  269.            XNClientWindow, win,
  270.            XNPreeditAttributes, list,
  271.            XNStatusAttributes, list,
  272.            NULL);
  273.     XFree(list);
  274.     if (ic == NULL) {
  275.     (void) fprintf(stderr, "Couldn't create input context\n");
  276.     exit(1);
  277.     }
  278.  
  279.     XGetICValues(ic, XNFilterEvents, &im_event_mask, NULL);
  280.     XSelectInput(dpy,win, ExposureMask | KeyPressMask
  281.          | StructureNotifyMask | im_event_mask);
  282.  
  283.     XSetICFocus(ic);
  284.  
  285.     XMapWindow(dpy,win);
  286.  
  287.     while(1) {
  288.     int buf_len = 10;
  289.     wchar_t *buffer = (wchar_t *)malloc(buf_len * sizeof(wchar_t));
  290.     int len;
  291.     KeySym keysym;
  292.     Status status;
  293.     Bool redraw = False;
  294.  
  295.     XNextEvent(dpy, &event);
  296.     if (XFilterEvent(&event, None))
  297.         continue;
  298.  
  299.         switch (event.type) {
  300.         case Expose:
  301.         /* draw the string at a hard-coded location */
  302.         if (event.xexpose.count == 0)
  303.         XwcDrawString(dpy, win, fontset, gc, 10, 50, string, str_len);
  304.             break;
  305.         case KeyPress:
  306.         len = XwcLookupString(ic, &event, buffer, buf_len,
  307.                   &keysym, &status);
  308.         /*
  309.          * Workaround:  the Xsi implementation of XwcLookupString
  310.          * returns a length that is 4 times too big.  If this bug
  311.          * does not exist in your version of Xlib, remove the
  312.          * following line, and the similar line below.
  313.          */
  314.         len = len / 4;
  315.  
  316.         if (status == XBufferOverflow) {
  317.         buf_len = len;
  318.         buffer = (wchar_t *)realloc((char *)buffer,
  319.                         buf_len * sizeof(wchar_t));
  320.         len = XwcLookupString(ic, &event, buffer, buf_len,
  321.                       &keysym, &status);
  322.         /* Workaround */
  323.         len = len / 4;
  324.         }
  325.         
  326.         redraw = False;
  327.         
  328.         switch (status) {
  329.         case XLookupNone:
  330.         break;
  331.         case XLookupKeySym:
  332.         case XLookupBoth:
  333.         /* Handle backspacing, and <Return> to exit */
  334.         if ((keysym == XK_Delete) || (keysym == XK_BackSpace)) {
  335.             if (str_len > 0) str_len--;
  336.             redraw = True;
  337.             break;
  338.         }
  339.         if (keysym == XK_Return) exit(0);
  340.         if (status == XLookupKeySym) break;  
  341.         case XLookupChars:
  342.         for(i=0; i < len; i++) 
  343.             string[str_len++] = buffer[i];
  344.         redraw = True;
  345.         break;
  346.         }
  347.         
  348.         /* do a very simple-minded redraw, if needed */
  349.         if (redraw) {
  350.         XClearWindow(dpy, win);
  351.         XwcDrawString(dpy, win, fontset, gc, 10, 50, string, str_len);
  352.         }
  353.         break;
  354.     case ConfigureNotify:
  355.         /*
  356.          * When the window is resized, we should re-negotiate the
  357.          * geometry of the Preedit and Status area, if they are used
  358.          * in the interaction style.
  359.          */
  360.         if (best_style & XIMPreeditArea) {
  361.         preedit_area.width = event.xconfigure.width*4/5;
  362.         preedit_area.height = 0;
  363.         GetPreferredGeometry(ic, XNPreeditAttributes, &preedit_area);
  364.         preedit_area.x = event.xconfigure.width - preedit_area.width;
  365.         preedit_area.y = event.xconfigure.height - preedit_area.height;
  366.         SetGeometry(ic, XNPreeditAttributes, &preedit_area);
  367.         }
  368.         if (best_style & XIMStatusArea) {
  369.         status_area.width = event.xconfigure.width/5;
  370.         status_area.height = 0;
  371.         GetPreferredGeometry(ic, XNStatusAttributes, &status_area);
  372.         status_area.x = 0;
  373.         status_area.y = event.xconfigure.height - status_area.height;
  374.         SetGeometry(ic, XNStatusAttributes, &status_area);
  375.         }
  376.         break;
  377.         }
  378.     }
  379. }
  380.  
  381.  
  382.